<!--
/* @license
 * Copyright 2020 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-->
<!DOCTYPE html>
<html lang="en">
<head>
  <title>&lt;model-viewer&gt; Lighthouse Example</title>
  <meta charset="utf-8">
  <meta name="description" content="Performance optimization for &lt;model-viewer&gt;">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="shortcut icon" type="image/png" href="../assets/favicon.png"/>
  <link type="text/css" href="../styles/examples.css" rel="stylesheet" />

  <script defer src="https://web3dsurvey.com/collector.js"></script>
  <script>
    window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
    ga('create', 'UA-169901325-1', { 'storage': 'none' });
    ga('set', 'referrer', document.referrer.split('?')[0]);
    ga('set', 'anonymizeIp', true);
    ga('send', 'pageview');
  </script>
  <script async src='https://www.google-analytics.com/analytics.js'></script>

  <style>
    html {
      height:100%;
    }

    body {
      height: 100%;
      margin: 0;
      background-color: #f7f7f7;
      font-family: 'Rubik', sans-serif;
      font-size: 16px;
      line-height: 24px;
      color: rgba(0,0,0,.87);
      font-weight: 400;
      -webkit-font-smoothing: antialiased;
    }

    p {
      max-width: 700px;
      margin: 1em;
      text-align: left;
    }

    model-viewer {
      display: block;
      width: 100vw;
      height: 100vw;
      max-width: 600px;
      max-height: 600px;
    }

    /* This keeps child nodes hidden while the element loads, except the poster */
    :not(:defined) {
      display: none;
    }

    .center-pre-prompt {
      display: flex;
      justify-content: center;
      align-items: center; 
      height: 100%;
      top: 0;
      left: 0;
      pointer-events: none;
    }

    .center-pre-prompt.hide {
      display: none;
    }

    @keyframes wiggle {	
      10%, 12% { transform: translateX(-25px); }	 
      30%, 32% { transform: translateX(25px); }	
      0%, 45%, 100% { transform: translateX(0%); }
    }	
    
    @keyframes fade {	
      5%, 40% {	opacity: 1; }	
      0%, 45%, 100% {	opacity: 0; }	 
    }

    .pre-prompt {
      pointer-events: none;
      animation-name: wiggle, fade;
      animation-duration: 5s;
      animation-iteration-count: infinite;
      animation-timing-function: ease-in-out;
    }

    .progress-bar {
      display: block;
      width: 33%;
      height: 10%;
      max-height: 2%;
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate3d(-50%, -50%, 0);
      border-radius: 25px;
      box-shadow: 0px 3px 10px 3px rgba(0, 0, 0, 0.5), 0px 0px 5px 1px rgba(0, 0, 0, 0.6);
      border: 1px solid rgba(255, 255, 255, 0.9);
      background-color: rgba(0, 0, 0, 0.5);
    }
 
    .progress-bar.hide {
      visibility: hidden;
      transition: visibility 0.3s;
    }
 
    .update-bar {
      background-color: rgba(255, 255, 255, 0.9);
      width: 0%;
      height: 100%;
      border-radius: 25px;
      float: left;
      transition: width 0.3s;
    }

    .ar-button {
      background-image: url(../assets/ic_view_in_ar_new_googblue_48dp.png);
      background-repeat: no-repeat;
      background-size: 20px 20px;
      background-position: 12px 50%;
      background-color: #fff;
      position: absolute;
      left: 50%;
      transform: translateX(-50%);
      white-space: nowrap;
      bottom: 16px;
      padding: 0px 16px 0px 40px;
      font-family: Roboto Regular, Helvetica Neue, sans-serif;
      font-size: 14px;
      color:#4285f4;
      height: 36px;
      line-height: 36px;
      border-radius: 18px;
      border: 1px solid #DADCE0;
    }

    .ar-button:active {
      background-color: #E8EAED;
    }

    .ar-button:focus {
      outline: none;
    }

    .ar-button:focus-visible {
      outline: 1px solid #4285f4;
    }

    @keyframes circle {
      from { transform: translateX(-50%) rotate(0deg) translateX(50px) rotate(0deg); }
      to   { transform: translateX(-50%) rotate(360deg) translateX(50px) rotate(-360deg); }
    }

    @keyframes elongate {
      from { transform: translateX(100px); }
      to   { transform: translateX(-100px); }
    }

    model-viewer > .ar-prompt {
      position: absolute;
      left: 50%;
      bottom: 175px;
      animation: elongate 2s infinite ease-in-out alternate;
      display: none;
    }

    model-viewer[ar-status="session-started"] > .ar-prompt {
      display: block;
    }

    model-viewer > .ar-prompt > img {
      animation: circle 4s linear infinite;
    }
    .icon-modelviewer-black {
    background-image: url(../assets/ic_modelviewer.svg);
    }
    .icon-button {
      margin-left: -4px;
      margin-right: 8px;
      width: 34px;
      height: 34px;
      background-size: 34px;
    }
    .inner-home {
      display: flex;
      align-items: center;
      font-size: 1.1em;
      text-decoration: none;
    }
    .home {
      padding: 20px;
      overflow: auto;
      white-space: nowrap;
    }
    .lockup {
      display: flex;
      align-items: center;
      margin-bottom: 6px;
      color: rgba(0,0,0,.87);
    }
    .attribute {
      white-space: pre-wrap !important;
      font-family: 'Roboto Mono', monospace;
      color: black;
    }
    .attribute:hover {
      text-decoration: underline;
      color: #444444;
    }
  </style>
</head>
<body>
  <div class="home lockup">
    <a href="../" class="sidebar-mv inner-home">
      <div class="icon-button icon-modelviewer-black inner-home"></div>
      <div class="inner-home"><span class="attribute">&lt;model-viewer&gt;</span></div>
    </a>
  </div>
  <div align="center">
  <!-- First Example -->

    <h2>Setting up <code>&lt;model-viewer&gt;</code> for decent Lighthouse scores</h2>
  
    <model-viewer
        id="first"
        src="../assets/ShopifyModels/Chair.glb"
        poster="../assets/ShopifyModels/ChairPoster.webp"
        reveal="manual"
        camera-orbit="1.2195rad 1.362rad 100%"
        interaction-prompt-threshold="0"
        shadow-intensity="1"
        ar
        camera-controls
        generate-schema
        alt="3D model of a chair with footrest"
      >

      <div class="center-pre-prompt">
        <img class="pre-prompt" src="../assets/prompt.svg">
      </div>

      <div class="progress-bar hide" slot="progress-bar">
        <div class="update-bar"></div>
      </div>

      <button slot="ar-button" class="ar-button">
        View in your space
      </button>
    
      <div class="ar-prompt">
        <img src="../assets/hand.png">
      </div>
    </model-viewer>

    <p>Unlike the <a href="lighthouse.html">previous example</a>, this page
    loads <code>model-viewer.min.js</code> immediately, which results in poorer
    lighthouse performance scores, as this bundle of JavaScript takes time to
    parse. We still make a strong effort to keep this bundle size down as much
    as possible to ensure this simpler type of loading still gets decent
    performance, as this method does not require custom posters and such.</p>

    <p>It is still important to delay the loading of the 3D model, as including
    its load time in the lighthouse scores will tend to destroy them. As such
    the first <code>&lt;model-viewer&gt;</code> uses
    <code>reveal="manual"</code> and calls <code>dismissPoster()</code> to
    cause loading to proceed. The second <code>&lt;model-viewer&gt;</code> does
    not require this as it lazy-loads by default since it is below the fold.</p>

    <img src="../assets/lighthouse2.png" style="object-fit: contain; width:100%; max-width:700px;" alt="Lighthouse mobile scores">

    <p>Note the performance is worse than before, at only 93
    for mobile (shown above), though still 100 on desktop.</p>  

    <!-- Second Example -->
    <model-viewer
      id="second"
      src="../assets/ShopifyModels/Mixer.glb"
      poster="../assets/ShopifyModels/MixerPoster.webp"
      camera-orbit="0.9677rad 1.2427rad 100%"
      interaction-prompt-threshold="0"
      shadow-intensity="1"
      ar
      camera-controls
      alt="3D model of a countertop mixer"
    >

      <div class="progress-bar hide" slot="progress-bar">
        <div class="update-bar"></div>
      </div>

      <button slot="ar-button" class="ar-button">
        View in your space
      </button>

      <div class="ar-prompt">
        <img src="../assets/hand.png">
      </div>
    </model-viewer>

  </div>
  <div style="margin-top:24px"></div>
  <div class="footer">
    <ul>
      <li>
        Chair, Mixer ©Copyright 2020 <a href="https://www.shopify.com/">Shopify
          Inc.</a>, licensed under <a
          href="https://creativecommons.org/licenses/by/4.0/">CC-BY-4.0</a>.
      </li>
    </ul>
    <div style="margin-top:24px;" class="copyright">©Copyright 2018-2025 Google Inc. Licensed under the Apache License 2.0.</div>
    <div id='footer-links'></div>
  </div>

  <script type="module" src="./built/docs-and-examples.js">
  </script>
  <script type="module">
    (() => { initFooterLinks();})();
  </script>

  <script type="module" src="../../../node_modules/@google/model-viewer/dist/model-viewer.js"></script>

  <script type="module">
    const body = document.body;

    const onInteraction = () => {document.querySelector('#first').dismissPoster();};

    body.addEventListener('mouseover', onInteraction, {once:true});
    body.addEventListener('touchmove', onInteraction, {once:true});
    body.addEventListener('scroll', onInteraction, {once:true});
    body.addEventListener('keydown', onInteraction, {once:true});

    const onProgress = (event) => {
      const progressBar = event.target.querySelector('.progress-bar');
      const updatingBar = event.target.querySelector('.update-bar');
      updatingBar.style.width = `${event.detail.totalProgress*100}%`;
      if (event.detail.totalProgress === 1) {
        progressBar.classList.add('hide');
      } else {
        progressBar.classList.remove('hide');
        if (event.detail.totalProgress === 0) {
          event.target.querySelector('.center-pre-prompt')?.classList.add('hide');
        }
      }
    };

    document.querySelector('#first').addEventListener('progress', onProgress);
    document.querySelector('#second').addEventListener('progress', onProgress);
  </script>
</body>
</html>
